package com.ntty.factory;

import java.io.File;
import java.util.concurrent.TimeUnit;

import com.ntty.client.CopyBigDataHandler;
import com.ntty.server.handle.HeartbeatHandler;
import com.ntty.server.handle.HelloWorldHandler;
import com.ntty.utils.MsgFrameDecoder;
import com.ntty.utils.bean.IMsg;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * 通用handle 处理(过滤)器工厂
 * 
 * @author 阿友 37798955@qq.com
 *
 */
public class PipelineFactory {
	/** 用自定义的CopyBigDataHandler 来保存大数据到指定文件 */
	public static void reg处理传入的大数据(ChannelPipeline pipeline, File 保存到) {

		pipeline.addLast(new CopyBigDataHandler(保存到));
	}

	/** helloWorld 测试 */
	public static void helloWorld(ChannelPipeline pipeline) {
		pipeline.addLast(new ObjectEncoder());
		pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
		pipeline.addLast(new HelloWorldHandler());
	}

	/**
	 * 处理空闲连接和超时是网络应用程序的核心部分。
	 * 
	 * 当发送一条消息后，可以检测连接是否还处于活跃状态，若很长时间没用了就可以断开连接。
	 * 
	 * Netty提供了很好的解决方案，有三种不同的ChannelHandler处理闲置和超时连接：
	 * 
	 * IdleStateHandler，当一个通道没有进行读写或运行了一段时间后出发IdleStateEvent
	 * ReadTimeoutHandler，在指定时间内没有接收到任何数据将抛出ReadTimeoutException
	 * WriteTimeoutHandler，在指定时间内有写入数据将抛出WriteTimeoutException
	 * 
	 * @author Administrator
	 *
	 */
	public static void reg空闲处理器(ChannelPipeline pipeline, int 间隔n秒) {
		if (间隔n秒 == 0)
			间隔n秒 = 60;
		pipeline.addLast(new IdleStateHandler(0, 0, 间隔n秒, TimeUnit.SECONDS));

	}

	/**
	 * IdleStateHandler 当一个通道没有进行读写或运行了一段时间后出发IdleStateEvent
	 * 
	 * 触发IdleStateHandler
	 */
	public static void reg空闲处理器(ChannelPipeline pipeline, int 读空闲秒数, int 间隔n秒) {
		pipeline.addLast(new IdleStateHandler(读空闲秒数, 0, 间隔n秒, TimeUnit.SECONDS));
	}

	/**
	 * IdleStateHandler 当一个通道没有进行读写或运行了一段时间后
	 * 
	 * 抛出 UserEventTriggered 事件 IdleStateEvent
	 * 
	 * 触发IdleStateHandler
	 */
	public static void reg空闲处理器(ChannelPipeline pipeline, int 读空闲秒数, int 写空闲秒数, int 间隔n秒) {
		// *,等待处理
		/*
		 * 第一个参数是指定读操作空闲秒数， 第二个参数是指定写操作的空闲秒数， 第三个参数是指定读写空闲秒数，
		 * 当有操作操作超出指定空闲秒数时，便会触发UserEventTriggered事件
		 */
		pipeline.addLast(new IdleStateHandler(读空闲秒数, 写空闲秒数, 间隔n秒, TimeUnit.SECONDS));
	}
	/* 解决粘包/拆包问题 regDecoderxxxx() */

	/*
	 * 常用的解决方案有: 1）消息定长; 2）在包尾增加回车换行符进行分割;
	 * 
	 * 3）将特殊的分隔符作为消息的结束符; 4）将消息分为消息头和消息体;
	 * 
	 * 5）更复杂的应用层协议。
	 * 
	 * 如FixedLengthFrameDecoder用于方案1，LineBasedFrameDecoder用于方案2，
	 * 
	 * DelimiterBasedFrameDecoder用于方案3，LengthFieldBasedFrameDecoder用于方案4。
	 * 
	 * 
	 */

	public static void regDecoder定长解码器(ChannelPipeline pipeline, int 固定长度) {
		// 解码后
		if (固定长度 == 0)
			固定长度 = 1024;
		pipeline.addLast(new FixedLengthFrameDecoder(固定长度));

	}

	public static void regDecoder换行解码器(ChannelPipeline pipeline, int 分割后内容最大长度) {
		// 解码后
		if (分割后内容最大长度 == 0)
			分割后内容最大长度 = 64 * 1024;
		pipeline.addLast(new LineBasedFrameDecoder(分割后内容最大长度));

	}

	public static void regDecoder分隔符解码器(ChannelPipeline pipeline, int 分割后内容最大长度, ByteBuf[] 分隔符) {
		// 参数1 分隔后每段的最大长度. 参数2:分隔符 =Delimiters .lineDelimiter();
		pipeline.addLast(new DelimiterBasedFrameDecoder(分割后内容最大长度, 分隔符));

	}

	/*
	 * MsgFrameDecoder 继承于 LengthFieldBasedFrameDecoder
	 * 
	 * 格式 header length body *
	 * 
	 * 例子:心跳包 包头 2字节(0xAA 0x55); 功能 1字节 0xF0表示心跳; 设备2字节;
	 * 
	 * 数据长度 2字节 (lengthField);
	 * 
	 * 系统时间7字节;校验码1字节; 包尾 2字节(0x55 0xAA );
	 * 
	 * maxFrameLength：单个包最大的长度，这个值根据实际场景而定，我设置的是1024，固然我的心跳包不大，但是其他包可能比较大。
	 * 
	 * lengthFieldOffset：表示数据长度字段开始的偏移量，比如上面的协议，lengthFieldOffset应该取值为5，
	 * 因为数据长度之前有2个字节的包头，1个字节的功能ID，2个字节的设备ID,一共为5。
	 * 
	 * lengthFieldLength：数据长度字段的所占的字节数，上面的协议中写的是2个字节，所以取值为2 修改帧数据长度字段中定义的值，可以为负数
	 * 因为有时候我们习惯把头部记入长度,若为负数,则说明要推后多少个字段
	 * 
	 * lengthAdjustment：数据长度字段后的内容字节数 这里取值为10=7(系统时间) + 1（校验码）+ 2(包尾)，
	 *
	 * 
	 * initialBytesToStrip： 解析时候跳过多少个长度
	 * 表示从整个包第一个字节开始，向后忽略的字节数，我设置为0，本来可以忽略掉包头的两个字节（即设置为2） *
	 * ，但是实际项目中，需要校验包头取值是否为AA55，来判断包的合法性。
	 * 
	 * failFast
	 * 为true，当frame长度超过maxFrameLength时立即报TooLongFrameException异常，为false，读取完整个帧再报异
	 * 
	 */
	public static void regDecoder消息解码器(ChannelPipeline pipeline, int 内容最大长度, int 数据长度字段开始的偏移量, int 数据长度字段的字节数,
			int 数据长度字段后的内容字节数) {

		pipeline.addLast(new MsgFrameDecoder(内容最大长度, 数据长度字段开始的偏移量, 数据长度字段的字节数));

	}
}
